
#include <stdio.h>
#include <gif_lib.h>
#include <BFile>

#include "ReadWrite_gif.h"

ReadWrite_gif::ReadWrite_gif()
{
	_author = "zym";
	_version = "1.0";
	_description = "This is BWE Plugin for reading *.bmp image file, write width libjpeg, thank a lot !";
}
ReadWrite_gif::~ReadWrite_gif()
{

}

void ReadWrite_gif::setOptions(const BString& options)
{
	_options = options;
}
const BString& ReadWrite_gif::options() const
{
	return _options;
}

const BString& ReadWrite_gif::author() const
{
	return _author;
}
const BString& ReadWrite_gif::version() const
{
	return _version;
}
const BString& ReadWrite_gif::description() const
{
	return _description;
}
const BString& ReadWrite_gif::message() const
{
	return _message;
}

bool ReadWrite_gif::readGifFile(GifFileType* gif_file, BObject* object)
{
	BImage* image = dynamic_cast<BImage*>(object);
	if (!image)
	{
		puts("gif target should be image.");
		return false;
	}

	int error_code = DGifSlurp(gif_file);
	if (error_code != GIF_OK)
	{
		return false;
	}

	int gif_width = gif_file->SWidth;
	int gif_height = gif_file->SHeight;
	GifWord gif_colors = (1 << gif_file->SColorResolution);

	Format format = image->format();
	if (format == Format_None)
	{
		SavedImage* frame = &gif_file->SavedImages[0];
		for (ExtensionBlock* ep = frame->ExtensionBlocks; ep < frame->ExtensionBlocks + frame->ExtensionBlockCount; ep++)
		{
			if (ep->Function == GRAPHICS_EXT_FUNC_CODE)
			{
				GraphicsControlBlock gcb;
				DGifExtensionToGCB(ep->ByteCount, ep->Bytes, &gcb);
				if (gcb.TransparentColor != NO_TRANSPARENT_COLOR)
				{
					format = Format_RGBA;
					break;
				}
			}
		}
		if (format == Format_None)
			format = Format_RGB;
		image->setFormat(format);
	}
	if (format == Format_RGB)
		_buffer.resize(gif_file->SWidth * gif_file->SHeight * 3);
	else if (format == Format_RGBA)
		_buffer.resize(gif_file->SWidth * gif_file->SHeight * 4);
	else
		return false;
	image->setSize(gif_file->SWidth, gif_file->SHeight);

	BMovie* movie = dynamic_cast<BMovie*>(object);

	int gif_loopcount = 0;
	BArray<GifByteType> screen_buf(gif_file->SWidth * gif_file->SHeight);
	memset(screen_buf.data(), gif_file->SBackGroundColor, sizeof(GifByteType) * screen_buf.size());
	int trans_color = -1;
	BReal duration = 0;
	for (int im = 0; im < gif_file->ImageCount; im++)
	{
		SavedImage* frame = &gif_file->SavedImages[im];
		for (int i = 0; i < frame->ExtensionBlockCount; i++)
		{
			ExtensionBlock& block = frame->ExtensionBlocks[i];
			if (block.Function == GRAPHICS_EXT_FUNC_CODE)
			{
				GraphicsControlBlock gcb;
				DGifExtensionToGCB(block.ByteCount, block.Bytes, &gcb);
				trans_color = gcb.TransparentColor;
				BReal interval = gcb.DelayTime * BReal(0.01);
				duration += interval;
			}
			else if (block.Function == APPLICATION_EXT_FUNC_CODE)
			{
				ExtensionBlock& next = frame->ExtensionBlocks[i];
				if (block.ByteCount >= 11 && next.ByteCount >= 3 && memcmp(next.Bytes, "NETSCAPE2.0", 11) == 0)
				{
					unsigned char* params = next.Bytes;
					unsigned int loopcount = params[1] | (params[2] << 8);
					gif_loopcount = loopcount;
				}
			}
		}

		ColorMapObject* color_map = (frame->ImageDesc.ColorMap ? frame->ImageDesc.ColorMap : gif_file->SColorMap);

		int left = frame->ImageDesc.Left;
		int top = frame->ImageDesc.Top;
		int width = frame->ImageDesc.Width;
		int height = frame->ImageDesc.Height;

		for (int i = top; i < top + height; i++)
		{
			memcpy(screen_buf.data() + i * gif_file->SWidth + left, frame->RasterBits + (i - top) * width, width);
			for (int c = left; c < left + width; c++)
			{
				int index = i * gif_file->SWidth + c;
				int color_index = screen_buf[index];
				GifColorType* gif_color = &color_map->Colors[color_index];
				if (format == Format_RGB)
				{
					BByte* rgb = _buffer.data(index * 3);
					if (trans_color >= 0 && trans_color == color_index)
					{
						continue;
					}
					rgb[0] = gif_color->Red;
					rgb[1] = gif_color->Green;
					rgb[2] = gif_color->Blue;
				}
				if (format == Format_RGBA)
				{
					BByte* rgb = _buffer.data(index * 4);
					if (trans_color >= 0 && trans_color == color_index)
					{
						rgb[3] = 0;
						continue;
					}
					rgb[0] = gif_color->Red;
					rgb[1] = gif_color->Green;
					rgb[2] = gif_color->Blue;
					rgb[3] = 255;
				}
			}
		}
		if (movie)
		{
			movie->addFrame(_buffer);
		}
		else
		{
			image->setPixels(_buffer);
			break;
		}
	}
	if (movie && duration > 0)
	{
		movie->setDuration(duration);
	}
	return true;
}

BObject* ReadWrite_gif::load(const BString& fileName)
{
	BMovie* movie = new BMovie();
	if (!load(fileName, movie))
	{
		delete movie;
		return 0;
	}
	BString name = fileName.name();
	name.remove(name.find('.'), name.size());
	movie->setName(name);
	return movie;
}
bool ReadWrite_gif::load(const BString& fileName, BObject* object)
{
	int error_code = 0;
	GifFileType* gif_file = DGifOpenFileName(fileName.cstr(), &error_code);
	if (gif_file == NULL)
	{
		const char* gif_error = GifErrorString(error_code);
		return false;
	}

	readGifFile(gif_file, object);

	if (BImage* image = dynamic_cast<BImage*>(object))
		image->setFileName(fileName);

	if (DGifCloseFile(gif_file, &error_code) == GIF_ERROR)
	{
		const char* error_str = GifErrorString(error_code);
		puts(error_str);
		return false;
	}
	return true;
}
bool ReadWrite_gif::save(const BString& fileName, const BObject* object) const
{
	int interval = 0;
	const int gif_width = 0;
	const int gif_height = 0;
	BList<BImage> gif_list;
	const BImage* image = dynamic_cast<const BImage*>(object);
	if (!image)
		return false;

	BArray<GifByteType> red_buffer(gif_width * gif_height);
	BArray<GifByteType> green_buffer(gif_width * gif_height);
	BArray<GifByteType> blue_buffer(gif_width * gif_height);

	int error_code;
	GifFileType* gif_file = EGifOpenFileName(fileName.cstr(), false, &error_code);
	if (gif_file == NULL)
	{
		const char* gif_error = GifErrorString(error_code);
		return false;
	}

	gif_file->SWidth = gif_width;
	gif_file->SHeight = gif_height;
	gif_file->SColorResolution = 8;
	gif_file->SBackGroundColor = 0;
	gif_file->SColorMap = NULL;
	gif_file->SavedImages = NULL;
	gif_file->ImageCount = 0;
	gif_file->ExtensionBlockCount = 0;
	gif_file->ExtensionBlocks = NULL;

	for (int i = 0; i < gif_list.size(); i++)
	{
		BImage img(gif_list.at(i));
		SavedImage* image = GifMakeSavedImage(gif_file, NULL);
		if (img.width() < gif_width || img.height() < gif_height)
			continue;
		for (int row = 0; row < gif_height; row++)
		{
			for (int col = 0; col < gif_width; col++)
			{
				red_buffer[row * gif_width + col] = img.pixelColor(col, row).r();
				green_buffer[row * gif_width + col] = img.pixelColor(col, row).g();
				blue_buffer[row * gif_width + col] = img.pixelColor(col, row).b();
			}
		}
		int map_size = (1 << gif_file->SColorResolution);
		if ((image->ImageDesc.ColorMap = GifMakeMapObject(map_size, NULL)) == NULL)
		{
			//"Failed to allocate memory required, aborted.";
			return false;
		}
		image->RasterBits = (GifPixelType*)malloc(sizeof(GifPixelType) * gif_width * gif_height);
		//if (GifQuantizeBuffer(gif_width, gif_height, &map_size,
		//	red_buffer.data(),
		//	green_buffer.data(),
		//	blue_buffer.data(),
		//	image->RasterBits,
		//	image->ImageDesc.ColorMap->Colors) == GIF_ERROR)
		//{
		//	return false;
		//}

		//"MakeSavedImage"<<i<<"mapsize"<<map_size;
		image->ImageDesc.Left = 0;
		image->ImageDesc.Top = 0;
		image->ImageDesc.Width = gif_width;
		image->ImageDesc.Height = gif_height;
		image->ImageDesc.Interlace = false;
		//image->ImageDesc.ColorMap = color_map;
		//image->RasterBits = out_buffer;

		GraphicsControlBlock gcb;
		gcb.DisposalMode = DISPOSAL_UNSPECIFIED;
		gcb.DelayTime = interval / 10;
		gcb.UserInputFlag = false;
		gcb.TransparentColor = NO_TRANSPARENT_COLOR;

		//"GCB To Saved"<<i;
		EGifGCBToSavedExtension(&gcb, gif_file, i);

		if (i == 0)
		{
			unsigned char params[3] = { 1, 0, 0 };
			//Create a Netscape 2.0 loop block
			if (GifAddExtensionBlock(&image->ExtensionBlockCount,
				&image->ExtensionBlocks,
				APPLICATION_EXT_FUNC_CODE,
				11, (unsigned char*)"NETSCAPE2.0") == GIF_ERROR)
			{
				//"out of memory while adding loop block.";
				return false;
			}
			//params[1] = (intval & 0xff);
			//params[2] = (intval >> 8) & 0xff;
			if (GifAddExtensionBlock(&image->ExtensionBlockCount,
				&image->ExtensionBlocks,
				0, sizeof(params), params) == GIF_ERROR)
			{
				return false;
			}
		}
	}

	//gif_file->ImageCount;
	int saved_count = gif_file->ImageCount;
	SavedImage* saved_images = gif_file->SavedImages;
	EGifSpew(gif_file);
	{
		SavedImage* sp;
		for (sp = saved_images;
			sp < saved_images + saved_count; sp++)
		{
			if (sp->ImageDesc.ColorMap != NULL)
			{
				GifFreeMapObject(sp->ImageDesc.ColorMap);
				sp->ImageDesc.ColorMap = NULL;
			}

			if (sp->RasterBits != NULL)
				free((char*)sp->RasterBits);

			GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
		}
		free((char*)saved_images);
		saved_images = NULL;
		sp = NULL;
	}

	return true;
}

int GifInputFunc(GifFileType* gif, GifByteType* buf, int len)
{
	BBuffer* buffer = (BBuffer*)gif->UserData;
	if (buffer->read(buf, len))
		return len;
	return 0;
}

BObject* ReadWrite_gif::read(const BBuffer& buffer)
{
	BMovie* movie = new BMovie();
	if (!read(buffer, movie))
	{
		delete movie;
		return 0;
	}
	return movie;
}
bool ReadWrite_gif::read(const BBuffer& buffer, BObject* object)
{
	BImage* image = dynamic_cast<BImage*>(object);
	if (!image)
		return false;

	int error_code = 0;
	GifFileType* gif_file = DGifOpen((void*)&buffer, GifInputFunc, &error_code);
	if (gif_file == NULL)
	{
		const char* gif_error = GifErrorString(error_code);
		return false;
	}

	readGifFile(gif_file, object);

	if (DGifCloseFile(gif_file, &error_code) == GIF_ERROR)
	{
		const char* error_str = GifErrorString(error_code);
		puts(error_str);
		return false;
	}
	return true;
}
bool ReadWrite_gif::write(BBuffer& buffer, const BObject* object) const
{
	const BImage* image = dynamic_cast<const BImage*>(object);
	if (!image)
		return false;

	Format format = image->format();
	const BByteArray* pixels = image->pixels();
	int pixelSize = image->width() * image->height();
	return true;
}
